﻿global using static Global;

using GitHubCopilotProxies.Handlers;

using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;

static class Global
{
    public const string TextContentType = "text/plain; charset=utf-8";
    public const string HtmlContentType = "text/html; charset=utf-8";
    public const string JsonContentType = "application/json; charset=utf-8";

    public static HttpContext ResponseWithXOAuthScopes(this HttpContext httpContext)
    {
        const string scopes = "gist, read:org, repo, user, workflow, write:public_key";
        httpContext.Response.Headers["X-OAuth-Scopes"] = scopes;
        return httpContext;
    }
    public static async Task ResponseJsonAsync(this HttpContext httpContext, string jsonString)
    {
        httpContext.Response.ContentType = JsonContentType;
        await httpContext.Response.WriteAsync(jsonString, Encoding.UTF8, httpContext.RequestAborted);
        await httpContext.Response.CompleteAsync();
    }
    public static async Task ResponseJsonAsync<T>(this HttpContext httpContext, T data) where T : class
    {
        var jsonString = data.SerializeToJsonString();
        await httpContext.ResponseJsonAsync(jsonString);
    }

    public static string SerializeToJsonString<T>(this T data) where T : class
    {
        return JsonSerializer.Serialize(data, AppJsonSerializerContext.Web.Options);
    }
    public static async Task<T?> DeserializeStreamAsync<T>(this HttpContext httpContext, Stream stream)
    {
        return await JsonSerializer.DeserializeAsync<T>(stream, AppJsonSerializerContext.Web.Options, httpContext.RequestAborted);
    }
}

public class ConfigCache<T> where T : class
{
    private T _config = default!;
    public T Get()
    {
        if (_config is SystemConfig.BaseModelConfig modelConfig)
            return (modelConfig.GetNext() as T)!;
        return _config;
    }
    public void Update(T newConfig) => _config = newConfig;
}
public record SystemConfig
{
    public CodeCompletionsConfig? CodeCompletions { get; set; }
    public CodeCompletionsConfig? EditCompletions { get; set; }
    public BaseModelConfig? Embeddings { get; set; }
    public BaseModelConfig? Chunks { get; set; }
    public string? ChatLanguage { get; set; }
    public Dictionary<string, ChatCompletionsConfig>? ChatCompletions { get; set; }

    public static TMC WithDefault<TMC>(TMC? config, TMC defaultConfig) where TMC : BaseModelConfig
    {
        if (config is null) return defaultConfig;
        if (config is { Model.Length: > 0, ApiUrl.Length: > 0 }) return config;
        if (config is { Model.Length: > 0 })
        {
            config.ApiUrl = defaultConfig.ApiUrl;
            config.ApiKey = defaultConfig.ApiKey;
            config.UseProxy = defaultConfig.UseProxy;
            return config;
        }
        return defaultConfig;
    }
    public ChatCompletionsConfig? GetChatCompletionsConfig(string modelName)
    {
        var gpt4oConfig = ChatCompletions!["gpt-4o"];
        if (modelName == "gpt-4o") return gpt4oConfig;
        var config = ChatCompletions.GetValueOrDefault(modelName);
        config ??= ChatCompletions.OrderByDescending(kv => kv.Key)
            .Where(kv => modelName.StartsWith(kv.Key!))
            .Select(kv => kv.Value).FirstOrDefault();
        if (config is not null) return SystemConfig.WithDefault(config, gpt4oConfig);
        return gpt4oConfig;
    }

    public record BaseModelConfig
    {
        public string? Model { get; set; }
        public string? ApiUrl { get; set; }
        public string? ApiKey { get; set; }
        public bool UseProxy { get; set; }


        private string[] _models = [];
        private string[] _apiKeys = [];
        private BaseModelConfig[] _combinations = []; // 存储预生成的组合
        private int _currentIndex = -1;

        public virtual BaseModelConfig GetNext() => GetNext<BaseModelConfig>();
        public virtual BaseModelConfig InitRoundRobin() => InitRoundRobin<BaseModelConfig>();

        protected TConfig GetNext<TConfig>() where TConfig : BaseModelConfig
        {
            // 如果组合少于等于1个，直接返回当前实例
            if (_combinations.Length <= 1) return (TConfig)this;
            int newIndex = Interlocked.Increment(ref _currentIndex);
            int safeIndex = (newIndex % _combinations.Length + _combinations.Length) % _combinations.Length;
            return (TConfig)_combinations[safeIndex];
        }
        protected TConfig InitRoundRobin<TConfig>() where TConfig : BaseModelConfig
        {
            static string[] SplitValues(string? input) => input?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) ?? [];
            _models = SplitValues(Model);
            _apiKeys = SplitValues(ApiKey);
            _currentIndex = -1; // 重置索引
            // 处理模型和密钥为空或只有一个的情况
            int modelCount = _models.Length;
            int keyCount = _apiKeys.Length;
            // 如果模型和密钥都只有一个或没有（即组合数<=1），则无需生成组合
            if (modelCount <= 1 && keyCount <= 1)
            {
                _combinations = [this]; // 组合就是自身
                return (TConfig)this;
            }
            // 计算笛卡尔积组合
            int totalCombinations = Math.Max(modelCount, 1) * Math.Max(keyCount, 1);
            _combinations = new BaseModelConfig[totalCombinations];
            int combinationIndex = 0;
            if (modelCount > 0)
                for (int i = 0; i < modelCount; i++)
                {
                    if (keyCount > 0)
                        for (int j = 0; j < keyCount; j++)
                            _combinations[combinationIndex++] = this with { Model = _models[i], ApiKey = _apiKeys[j] };
                    else // 只有模型，没有多个密钥
                        _combinations[combinationIndex++] = this with { Model = _models[i], ApiKey = ApiKey };
                }
            else if (keyCount > 0) // 只有密钥，没有多个模型
                for (int j = 0; j < keyCount; j++)
                    _combinations[combinationIndex++] = this with { Model = Model, ApiKey = _apiKeys[j] }; // Model保持不变
            return (TConfig)this;
        }
    }
    public record CodeCompletionsConfig : BaseModelConfig
    {
        public int MaxToken { get; set; }
        public DebounceConfig? Debounce { get; set; }
        public record DebounceConfig
        {
            public int Min { get; set; } = 250;
            public int Max { get; set; } = 750;
        }
        public override CodeCompletionsConfig GetNext() => base.GetNext<CodeCompletionsConfig>();
        public override CodeCompletionsConfig InitRoundRobin() => base.InitRoundRobin<CodeCompletionsConfig>();
    }
    public record ChatCompletionsConfig : BaseModelConfig
    {
        public string? Name { get; set; }
        public bool? Enabled { get; set; }
        public int MaxToken { get; set; }
        public SupportsConfig Supports { get; set; } = new SupportsConfig();
        public record SupportsConfig
        {
            public bool Stream { get; set; } = true;
            public bool Tools { get; set; }
        }
        public override ChatCompletionsConfig GetNext() => base.GetNext<ChatCompletionsConfig>();
        public override ChatCompletionsConfig InitRoundRobin() => base.InitRoundRobin<ChatCompletionsConfig>();
    }
}


[JsonSerializable(typeof(string[]))]
[JsonSerializable(typeof(float[]))]
[JsonSerializable(typeof(List<string>))]
[JsonSerializable(typeof(Dictionary<string, string>))]
[JsonSerializable(typeof(JsonElement))]
[JsonSerializable(typeof(SystemConfig))]
[JsonSerializable(typeof(SystemConfig.BaseModelConfig))]
[JsonSerializable(typeof(SystemConfig.CodeCompletionsConfig))]
[JsonSerializable(typeof(SystemConfig.CodeCompletionsConfig.DebounceConfig))]
[JsonSerializable(typeof(SystemConfig.ChatCompletionsConfig))]
[JsonSerializable(typeof(SystemConfig.ChatCompletionsConfig.SupportsConfig))]
[JsonSerializable(typeof(Dictionary<string, SystemConfig.ChatCompletionsConfig>))]
[JsonSerializable(typeof(Telemetry.TelemetryResponse))]
[JsonSerializable(typeof(CopilotCompletions.ChunkRequest))]
[JsonSerializable(typeof(CopilotCompletions.Chunk))]
[JsonSerializable(typeof(CopilotCompletions.Chunk.RangeNode))]
[JsonSerializable(typeof(CopilotCompletions.Chunk.ChunkEmbeddingNode))]
[JsonSerializable(typeof(CopilotCompletions.ChunkResponse))]
[JsonSerializable(typeof(CopilotCompletions.EmbeddingDataResponse))]
[JsonSerializable(typeof(CopilotCompletions.EmbeddingDataResponse.DataNode[]))]
internal partial class AppJsonSerializerContext : JsonSerializerContext
{
    public static readonly AppJsonSerializerContext Web = new AppJsonSerializerContext(new JsonSerializerOptions
    {
        WriteIndented = string.Equals(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"), "Development", StringComparison.OrdinalIgnoreCase),
        Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    });
}